/*
 * LoaderAC.cpp
 *
 *  Created on: May 4, 2011
 *      Author: alki
 */

#define LOADERAC_TAGS
#include "LoaderAC.h"
#include "../Exception.h"
#include <string>
#include <stack>

LoaderAC::LoaderAC() {
}

LoaderAC::~LoaderAC() {
}

void LoaderAC::LoadFile(string filename,vector<Objekt> &objekte,MaterialManager &matman) {
	this->m_file.open(filename.c_str());
	this->m_matman=&matman;

	string line;
	while(!this->m_file.eof()) {
		TAG tag=(TAG)this->ReadTagLine(line);
		if(tag==AC3D)
			this->ReadHeader(line);
		if(tag==MATERIAL)
			this->ReadMaterial(line);
		if(tag==OBJECT) {
			vector<string> split;
			this->strsplit(line,' ',split);

			if(split[1]=="world")
				this->ReadObject(world);
			else if(split[1]=="group")
				this->ReadObject(group);
			else if(split[1]=="poly")
				this->ReadObject(poly);
		}
	}

	cout << this->m_materialien.size() << " Materialien geladen" << endl;
	cout << this->m_objekte.size() << " Objekte geladen" << endl;

	this->m_file.close();

	for(unsigned int i=0;i<this->m_objekte.size();i++)
		objekte.push_back(this->m_objekte[i]);

	cout << "TEST2" << endl;
}

int LoaderAC::ReadTagLine(string &line) {
	getline(this->m_file,line);
	if(this->strbegins(line,"AC3D"))
		return AC3D;
	if(this->strbegins(line,"MATERIAL"))
		return MATERIAL;
	if(this->strbegins(line,"OBJECT"))
		return OBJECT;
	if(line.size()==0)
		return NUL;

	throw new Exception(NULL,__FILE__,__LINE__,"Fehler beim Einlesen der Daten","Unbekannter Zeilen-Tag",line);
}

void LoaderAC::ReadHeader(const string &line) {
	int v;
	sscanf(line.c_str(),"AC3D%x",&v);
	if(v>11)
		throw new Exception(NULL,__FILE__,__LINE__,"Fehler beim Einlesen der Daten","Datei-Version wird nicht unterstützt!",line);
}

void LoaderAC::ReadMaterial(const string &line) {
	vector<string> split;
	this->strsplit(line,' ',split);

	Material mat;

	for(unsigned int i=1;i<split.size();i++) {
		if(this->streq(split[i],"rgb")) {
			float r,g,b;
			sscanf(split[++i].c_str(),"%f",&r);
			sscanf(split[++i].c_str(),"%f",&g);
			sscanf(split[++i].c_str(),"%f",&b);
			mat.setLightDiffus(FARBE(r,g,b));
		}
		else if(this->streq(split[i],"amb")) {
			float r,g,b;
			sscanf(split[++i].c_str(),"%f",&r);
			sscanf(split[++i].c_str(),"%f",&g);
			sscanf(split[++i].c_str(),"%f",&b);
			mat.setLightAmbient(FARBE(r,g,b));
		}
		else if(this->streq(split[i],"emis")) {
			float r,g,b;
			sscanf(split[++i].c_str(),"%f",&r);
			sscanf(split[++i].c_str(),"%f",&g);
			sscanf(split[++i].c_str(),"%f",&b);
			mat.setLightEmissive(FARBE(r,g,b));
		}
		else if(this->streq(split[i],"spec")) {
			float r,g,b;
			sscanf(split[++i].c_str(),"%f",&r);
			sscanf(split[++i].c_str(),"%f",&g);
			sscanf(split[++i].c_str(),"%f",&b);
			mat.setLightSpecular(FARBE(r,g,b));

		}
		else if(this->streq(split[i],"shi")) {
			unsigned int exp;
			sscanf(split[++i].c_str(),"%d",&exp);
			mat.setSpecularExponent(exp);
		}
		else if(this->streq(split[i],"trans")) {
			float t;
			sscanf(split[++i].c_str(),"%f",&t);
			mat.setTransmission(t);
		}
		else if(this->streq(split[i],"reflec")) {
			float r;
			sscanf(split[++i].c_str(),"%f",&r);
			mat.setReflexion(r);
		}
		else if(this->streq(split[i],"refrac")) {
			float n;
			sscanf(split[++i].c_str(),"%f",&n);
			mat.setBrechung(n);
		}
	}

	this->m_materialien.push_back(mat);
}

void LoaderAC::ReadObject(int type) {
	if(type==world) {
		string line;
		getline(this->m_file,line);
		return;
	}

	int kids = 0;
	string line;

	stack<Vektor> pos;
	Vektor currpos;

	unsigned int vertexindex = 0;
	unsigned int currtex = 0;

	pos.push(Vektor(0,0,0));

	do {
		getline(this->m_file,line);
		if(this->strbegins(line,"name")) {
			// Name interessiert uns nicht
		}
		else if(this->strbegins(line,"data")) {
			// Data interessiert uns nicht
		}
		else if(this->strbegins(line,"crease")) {
			// Crease interessiert uns gerade auch noch nicht
		}
		else if(this->strbegins(line,"texture")) {
			char *filename=new char[line.size()-8];
			sscanf(line.c_str(),"texture %s",filename);
			string file;
			for(unsigned int i=1;i<line.size()-9;i++)
				file.push_back(filename[i]);
			delete [] filename;

			Textur tex;
			tex.Load(file);
			this->m_textur.push_back(tex);
			currtex++;
		}
		else if(this->strbegins(line,"texrep")) {
			// Texturwiederholungen unterstützen wir nicht
		}
		else if(this->strbegins(line,"rot")) {
			// Rotationsmatrix unterstützen wir nicht
		}
		else if(this->strbegins(line,"loc")) {
			float x,y,z;
			sscanf(line.c_str(),"loc %f %f %f",&x,&y,&z);
			currpos=currpos+Vektor(x,y,z);
			pos.pop();
			pos.push(currpos);
		}
		else if(this->strbegins(line,"url")) {
			// URL interessiert uns nicht
		}
		else if(this->strbegins(line,"numvert")) {
			unsigned int vert;
			sscanf(line.c_str(),"numvert %d",&vert);

			for(unsigned int i=0;i<vert;i++) {
				getline(this->m_file,line);
				VERTEX v;
				float x,y,z;
				sscanf(line.c_str(),"%f %f %f",&x,&y,&z);
				this->m_vertices.push_back(Vektor(x,y,z)+currpos);
				Vektor test=this->m_vertices.back();
			}
		}
		else if(this->strbegins(line,"numsurf")) {
			unsigned int s;
			sscanf(line.c_str(),"numsurf %d",&s);
			for(;s>0;s--) {
				SURFACE face;
				face.tex=currtex;

				getline(this->m_file,line); // SURF %d
				getline(this->m_file,line); // mat %d
				unsigned int mat;
				sscanf(line.c_str(),"mat %d",&mat);
				face.material=mat;
				getline(this->m_file,line); // refs %d
				unsigned int v;
				sscanf(line.c_str(),"refs %d",&v);
				for(;v>0;v--) {
					getline(this->m_file,line);
					unsigned int ref;
					float u,v;
					sscanf(line.c_str(),"%d %f %f",&ref,&u,&v);
					face.vertex.push_back(ref+vertexindex);
					face.u.push_back(u);
					face.v.push_back(v);
				}
				this->m_surfaces.push_back(face);
			}
		}
		else if(this->strbegins(line,"kids")) {
			unsigned int k;
			sscanf(line.c_str(),"kids %d",&k);
			kids+=k-1;

			currpos=currpos-pos.top();
			pos.pop();
			pos.push(Vektor(0,0,0));

			vertexindex=this->m_vertices.size();
		}
	} while(kids>=0);

	Objekt obj;

	this->CreateObject(&obj);

	this->m_objekte.push_back(obj);
	cout << "Objekt mit " << obj.getVertexCount() << " Vertices und " << obj.getTriangleCount() << " Triangles gefunden" << endl;
}

void LoaderAC::CreateObject(Objekt *obj) {
	if(obj==NULL) {
		throw new Exception(NULL,__FILE__,__LINE__,"CreateObject fehlgeschlagen","kein Zielobjekt definiert!");
	}

	for(unsigned int s=0;s<this->m_surfaces.size();s++) {
		TRIANGLE tri;
		for(unsigned int v=0;v<this->m_surfaces[s].vertex.size();v++) {
			tri.points[v]=obj->getVertexCount();
			VERTEX vert;
			vert.pos=this->m_vertices[this->m_surfaces[s].vertex[v]];
			vert.u=this->m_surfaces[s].u[v];
			vert.v=this->m_surfaces[s].v[v];

			Material mat=this->m_materialien[this->m_surfaces[s].material];
			vert.mat=this->m_matman->addMaterial(mat);
			obj->AddVertex(vert);
		}
		obj->AddTriangle(tri);
	}
}
